iT邦幫忙

2024 iThome 鐵人賽

DAY 13
0
JavaScript

Signal API in Angular系列 第 13

Day 13 - 將路由資料綁定到 Signal Input

  • 分享至 

  • xImage
  •  

在 Angular 16 中,withComponentInput 可以將路由資料綁定到Input decorator。相同的功能也適用於signal input

以下路由資訊可以綁定到Signal Input訊號輸入:

  • 查詢參數 (Query Parameters)
  • 參數 (Parameters)
  • 路線資訊 (Route Data)
  • 解析資訊 (Resolved Data)

建立 lazy-loaded 組件的路由

// route-data.routes.ts

import { Route } from '@angular/router';
import { getPerson } from './star-war.api';

export const routeDataRoutes: Route[] = [
 {
   path: '',
   loadComponent: () => import('./home.component'),
 },
 {
   path: 'route-path-param/:id',
   loadComponent: () => import('./route-path-param.component'),
   data: {
     jedi: 'Luke Skywalker',
     sith: 'Darth Maul'
   },
   resolve: {
     vader: () => getPerson(4),
   }
 },
 {
   path: 'component-input-precedence/:name/p/:id',
   loadComponent: () => import('./component-input-precedence.component'),
   data: {
     name: 'b',
   },
   resolve: {
     name: () => Promise.resolve('c')
   }
 }
];

routeDataRoutes延遲載入具有路由資料 (route data) 的組件。 route-path-param/:id 路徑載入 RoutePathParamComponent 組件,而 component-input-precedence/:name/p/:id 路徑載入 ComponentInputPrecedenceComponent 組件。

import { Route } from '@angular/router';
import { routeDataRoutes } from './route-data.routes';
export const routes: Route[] = [
 {
   path: 'home',
   loadChildren: () => routeDataRoutes
 },
 {
   path: '',
   pathMatch: 'full',
   redirectTo: '/home',
 },
 {
   path: '**',
   redirectTo: '/home'
 }
]

routeDataRoutes 成為 /home 路徑的子路由。

啟用 componentInputBinding

import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';

export const appConfig = {
 providers: [
   provideRouter(routes, withComponentInputBinding()),
   … other providers …
 ]
}

provideRouter函數中,第一個參數是路由,第二個參數是withComponentInputBinding函數。

在 Home 組件中新增 RouterLink 屬性

import { Component, ChangeDetectionStrategy } from '@angular/core';
import { RouterOutlet, RouterLink } from '@angular/router';

@Component({
 selector: 'app-home',
 standalone: true,
 imports: [RouterOutlet, RouterLink],
 template: `
   <nav>
     <a [routerLink]="['route-path-param', '100']" [queryParams]="queryParams">Lazy-load Route Data</a>
     <a [routerLink]="['component-input-precedence', 'a', 'p', '2']" [queryParams]="queryParams2">Component Input Precedence</a>
   </nav>
   <router-outlet />
 `,
})
export default class HomeComponent {
 queryParams = {
   movie: 'Star War',
   director: 'George Lucas'
 };

 queryParams2 = {
   id: '1'
 }
}

導覽列新增RouterLink屬性,用於將參數 (Parameters) 和查詢參數 (Query Parameters) 綁定到路徑 (Route Path)。 該組件將 queryParamsparam 100 綁定到route-path-param 路徑。它還將 queryParams2 和兩個參數 a2 綁定到Component Input Precedence路徑。

將路由資料綁定到 Signal Inputs

{
   path: 'route-path-param/:id',
   loadComponent: () => import('./route-path-param.component'),
   data: {
     jedi: 'Luke Skywalker',
     sith: 'Darth Maul'
   },
   resolve: {
     vader: () => getPerson(4),
   }
 },
export default class RoutePathParamComponent {
 // path param
 id = input();

 // query parameters
 movie = input<string>();
 director = input<string>();

 // route data
 jedi = input<string>();

 // route data
 sith = input<string>();

 // route resolver
 vader = input<Person | undefined>();
}

id 參數映射到 id signal input。 查詢參數 moviedirector 被映射到 moviedirector signal inputs。路線資料 jedisith 被映射到 jedisith signal inputs。 Resolver 呼叫 getPerson 函數並將結果儲存到 vader signal input。

template: `
   <app-component-input-container>
     <h3>Signal Inputs from activated routes</h3>
     <p>Path Param: {{ id() }}</p>
     <p class="title">Query Params</p>
     <div class="border">
       <p>Movie: {{ movie() }}</p>
       <p>Director: {{ director() }}</p>
     </div>

     <p class="title">Route Data</p>
     <div class="border">
       <p>Jedi: {{ jedi() }}</p>
       <p>Sith: {{ sith() }}</p>
     </div>

     @let evil = vader();
     @if (evil) {
       <p class="title">Resolved Data</p>
       <div class="border">
         <p>Name: {{ evil.name }}</p>
         <p>Height: {{ evil.height }}</p>
         <p>Mass: {{ evil.mass }}</p>
       </div>
     }
</app-component-input-container>

https://ithelp.ithome.com.tw/upload/images/20240822/20168314Eak73OudYH.png

ComponentInputBinding 的優先權

根據withComponentInputBinding 的來源碼來看,資料 (route data) 優先權最高,參數 (parameter) 次之,查詢參數 (query parameter) 優先權最低。 如果路由資料和查詢參數具有相同的鍵,則 signal input 將與路由資料綁定。

{
   path: 'component-input-precedence/:name/p/:id',
   loadComponent: () => import('./component-input-precedence.component'),
   data: {
     name: 'b',
   },
   resolve: {
     name: () => Promise.resolve('c')
   }
 }
queryParams2 = {
   id: '1'
}
export default class ComponentInputPrecedenceComponent {
 // path param
 id = input();

 // resolved data
 name = input<string>();

 activatedRoute = inject(ActivatedRoute);

 queryParamsId = toSignal(this.activatedRoute.queryParamMap.pipe(
       map((params) => params.get('id'))), { initialValue: '' });

 paramsId = toSignal(this.activatedRoute.paramMap.pipe(
       map((params) => params.get('id'))), { initialValue: '' });

 paramsName = toSignal(this.activatedRoute.paramMap.pipe(
       map((params) => params.get('name'))), { initialValue: '' });

 dataName = toSignal(this.activatedRoute.data.pipe(
       map((params) => params['name'])), { initialValue: ''});
}

在組件中,我注入ActivatedRoute以從目前路由中提取查詢參數 (query parameter)、參數 (parameter) 和路由資料 (route data)。 queryParamsIdparamsIdid 分別為 1、2 和 2。 dataNamename 分別是 ac

<app-component-input-container>
     <h3>Component Input Binding Precedence</h3>
     <p>Route data precedence: data > param > query param</p>
     <p>Query Param Id: {{ queryParamsId() }}, Param Id: {{ paramsId() }}, Input Id: {{ id() }}</p>
     <p>Param Id: {{ paramsName() }}, Data: {{ dataName() }}, Input Name: {{ name() }}</p>
</app-component-input-container>

https://ithelp.ithome.com.tw/upload/images/20240822/20168314J7vasnDBDj.png

Alternatives to componentInputBinding

如果您不想啟用 withComponentInputBinding 功能,那麼您可以考慮安裝 ngxtension 函式庫並使用其函數來取得路由資訊 (route data) 作為 signal

  • injectParams - 使開發人員能夠從目前路由注入參數作為 signal
  • injectQueryParams - 使開發人員能夠從目前路由注入查詢參數作為 signal
  • injectRouteData - 使開發人員能夠將目前路由的資料作為 signal

結論:

  • 啟用 withComponentInputBinding 功能後,路由參數、查詢參數、資料和解析資料可以綁定到 Signal Input。
  • 如果選擇退出 withComponentInputBinding 功能,可以注入 ActivatedRoute,從目前路由中提取值並使用 toSignal 建立 signal
  • componentInputBinding的優先權從高到低依序是路由資料、路由參數、路由查詢參數。
  • ngxtension 函式庫 (library) 包含從目前路由 (current route) 注入路由資訊 (route data) 的實用函數 (utility functions)。當開發人員不想啟用 withComponentInputBinding 時,這是一種替代方案。

鐵人賽第13天就這樣結束了。

參考:


上一篇
Day 12 - 介紹 Signal Input
下一篇
Day 14 - Use signal and signal inputs in a host element
系列文
Signal API in Angular39
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言